Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Add no_entry attribute to omit entry point symbol #2735

Closed
wants to merge 1 commit into from

Conversation

joshtriplett
Copy link
Member

This allows people to write their own entry point, without needing to
manually link or create a .cargo/config with target-specific linker
arguments.

This allows people to write their own entry point, without needing to
manually link or create a `.cargo/config` with target-specific linker
arguments.
@joshtriplett joshtriplett added A-ffi FFI related proposals. T-lang Relevant to the language team, which will review and decide on the RFC. labels Jul 30, 2019
@joshtriplett
Copy link
Member Author

cc @retep998

As discussed on Discord, I added an explanation of the behavior on platforms that can't support no_entry:

Some targets may be able to support invoking the C library without running the startup code provided by the C library; other targets may not support this, or have limitations on such support. Targets that do not support this should document the target-specific limitations of no_entry for their target, may require no_std in addition to no_entry, or may not support no_entry at all.

text/0000-no-entry.md Show resolved Hide resolved
text/0000-no-entry.md Show resolved Hide resolved
binary. When linking, Rust will pass any necessary target-specific arguments to
the toolchain to omit the target's entry-point code, such as `-nostartfiles`.

Binaries built with this attribute set will typically not link unless the user
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Elaborate on typically?

text/0000-no-entry.md Show resolved Hide resolved
text/0000-no-entry.md Show resolved Hide resolved

We could make this a rustc command-line option, rather than an attribute.
However, that would separate the configuration required to build a crate from
the crate itself. We have substantial precedent for including such
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is true but enumerate some examples nonetheless?

text/0000-no-entry.md Show resolved Hide resolved
text/0000-no-entry.md Show resolved Hide resolved
text/0000-no-entry.md Show resolved Hide resolved
[future-possibilities]: #future-possibilities

We should simplify the declaration of an entry-point symbol, such as by
providing an `#[entry]` attribute. The symbol itself would still have a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have #[entry] on unsafe ... fn foobar(...) do we then also need #![no_entry]?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Centril Yes, we do. entry could be used in a library, for instance, with a binary crate using no_entry and using that library. I'll add a note that it would make sense to have a lint if you use entry in a binary crate without using no_entry, though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joshtriplett Ah I see; so this is akin to "needs / provides" of #![requires_allocator] (because every program requires an entry point) and #[global_allocator]. -- Please mention this similarity explicitly as well as the use-case for library/binary division in the text also. :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by requires_allocator? I don't see any references to that or similar names anywhere on the web.

The RFC already mentions the use case of library/binary division, in the rationale and alternatives section: "However, this would make it difficult to flexibly provide the entry point from a library or other mechanism."

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by requires_allocator? I don't see any references to that or similar names anywhere on the web.

Ooops; It's called #![needs_allocator] in https://github.com/rust-lang/rust/blob/master/src/liballoc/lib.rs#L65 apparently.

The RFC already mentions the use case of library/binary division, in the rationale and alternatives section: "However, this would make it difficult to flexibly provide the entry point from a library or other mechanism."

Yes, but connecting this to entry and no_entry is another matter, it would be helpful to clarify that this is what is referred to by that sentence.

@Centril Centril added A-main Proposals relating to program entrypoints. A-linkage Proposals relating to the linking step. T-compiler Relevant to the compiler team, which will review and decide on the RFC. labels Jul 31, 2019
@Amanieu
Copy link
Member

Amanieu commented Jul 31, 2019

My main objection to this RFC is that this attribute should be described as part of the target specification rather than as an attribute. All of the use cases that you described will run without any libc, and therefore will want to use a custom target rather than one of the existing targets. As I noted in my comment on the pre-RFC thread, you can already do what is proposed in this RFC by adding -nostdlib to the pre-link-args section of the target json, without needing to mess with .cargo/config.

@fintelia
Copy link

It would be really helpful for this RFC to describe of exactly what functions are called during program startup under each valid configuration of #![no_main] × #![no_start] × #![no_std] × #![no_core] × ![no_entry]. From what I can tell, the order seems to be something like the following. (At any point the user can replace a function and chose to either skip the rest of setup or continue down the list):

  1. Linker provided function named something like _start (replaceable by naming a different entrypoint symbol via linker args or script, or by using the same one and setting #![no_entry])
  2. glibc initialization function or similar
  3. main function (replaceable by setting #[no_main] and providing a symbol with the right name)
  4. lang_start function (replaceable with #[start] + #![no_start])
  5. user provided main function (renamed with #[main])

Based on that list, I'd be curious to understand why it wouldn't be enough to just add an attribute that said "set #![no_start] and make (possibly extern) #[start] item the executable entry point".

@joshtriplett
Copy link
Member Author

@Amanieu

My main objection to this RFC is that this attribute should be described as part of the target specification rather than as an attribute.

I commented on that in the pre-RFC thread: target specifications are unstable, and likely to remain so for the foreseeable future since they fundamentally depend on LLVM and LLVM internals. In addition, this would require specifying a custom target, and potentially building core/std for that target, which will again require nightly for the foreseeable future.

All of the use cases that you described will run without any libc, and therefore will want to use a custom target rather than one of the existing targets.

That does not describe my use cases. I intend to use this on existing linux targets as well, for instance.

by adding -nostdlib to the pre-link-args section of the target json

What target json? I'm not looking to use this exclusively for custom targets, nor do I want to permanently require nightly. Target json is a strictly worse solution for many use cases than .cargo/config; I'm looking to build a better solution than .cargo/config.

@comex
Copy link

comex commented Aug 1, 2019

Point of clarification:

On Darwin, executable startup used to follow a scheme similar to Linux, with a _start symbol defined by crt0. But years ago it was replaced with a simpler design, where executables directly encode the address of main (in a load command called LC_MAIN), and there is no crt0 at all.

What, if anything, should #[no_entry] (or a hypothetical #[entry]) do on this platform?

@joshtriplett
Copy link
Member Author

(Current status: revising RFC based on feedback, will post new version that addresses comments.)

@joshtriplett
Copy link
Member Author

@comex Can you still write binaries that don't use libc at all? (It sounds like you can't use libc without the entry-point code like Linux's -nostartfiles, but can you write non-libc-based binaries, the equivalent of -nostdlib?)

@fintelia
Copy link

fintelia commented Sep 1, 2019

@joshtriplett Are you still in the process of making revisions?

@comex
Copy link

comex commented Sep 3, 2019

@joshtriplett LC_MAIN is handled by dyld, which is actually separate from libc aka libSystem. However, separately from that, the linker forbids creating dynamically linked executables that don't link to libSystem:

% clang -o foo foo.c -nostdlib
ld: dynamic main executables must link with libSystem.dylib for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

On the other hand, I just checked, and it looks like it's still possible to create statically linked executables using -static. In this mode, the linker will emit LC_UNIXTHREAD instead of LC_MAIN, i.e. the old crt-based startup scheme. By default it expects the entry point to be named start (no underscore, not even the one that gets prepended to all symbols by default).

This is not particularly useful, though, since libSystem's system call wrappers are the only supported/stable way to perform system calls; even Go switched from emitting static binaries to linking against libSystem a few years ago last year.

symbols.

Existing programming languages rely entirely on the toolchain for this
functionality. Introducing `no_entry` makes it possible to gain control from
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you provide a minimal example of how C (e.g. using clang) handles this? Do C compilers offer compiler extensions to make this simpler ?

@joshtriplett
Copy link
Member Author

While I'm still interested in seeing this happen, this needs some redesign to address portability (and handle platforms where you can't use libc without its entry point). I'm going to close this for now, with an eye towards opening something new in the future if possible.

@matklad
Copy link
Member

matklad commented Oct 6, 2021

I've played with -nostartfiles today, and I noticed that my program fails to link with the error about not finding the memset function. This is related to https://github.com/rust-lang/compiler-builtins -- basically, besides entry point handling, we also rely on "startfilies" libraries for some intrinsic operations like memset.

So, to stabilize something along this lines, it seems like we should also make sure that rustc doesn't rely on external compiler-builtins.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ffi FFI related proposals. A-linkage Proposals relating to the linking step. A-main Proposals relating to program entrypoints. T-compiler Relevant to the compiler team, which will review and decide on the RFC. T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants